package io.zhiller.simpletemplate.plugin.update

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Downloading
import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import androidx.core.content.FileProvider
import io.zhiller.simpletemplate.api.file_operation.DownloadFileInfo
import io.zhiller.simpletemplate.api.file_operation.DownloadFileStatus
import io.zhiller.simpletemplate.api.file_operation.FileApis
import io.zhiller.simpletemplate.api.git_repository.GitApis
import io.zhiller.simpletemplate.api.git_repository.ReleaseDTO
import io.zhiller.simpletemplate.config.GlobalConfig
import io.zhiller.simpletemplate.ui.component.ButtonWarning
import io.zhiller.simpletemplate.ui.theme.inversePrimaryLight
import io.zhiller.simpletemplate.ui.theme.primaryContainerLight
import io.zhiller.simpletemplate.utils.vanilla.DialogUtils
import io.zhiller.simpletemplate.utils.vanilla.FileUtils
import io.zhiller.simpletemplate.utils.vanilla.IntentUtils
import io.zhiller.simpletemplate.utils.vanilla.MsgUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.java.KoinJavaComponent.inject
import java.io.File


/**
 * @author Zhiller
 * @since 2023/9/10
 */
object UpdateManager {
  
  private val gitApis: GitApis by inject(GitApis::class.java)
  
  @SuppressLint("CoroutineCreationDuringComposition")
  @Composable
  fun UpdateComp(context: Context) {
    var downloadFileStatus by remember { mutableStateOf(DownloadFileStatus.INITIAL as DownloadFileStatus) }
    var currentProgress by remember { mutableIntStateOf(0) }
    var alreadyDownloadSize by remember { mutableLongStateOf(0L) }
    var status by remember { mutableStateOf("Download") }
    var enableButton by remember { mutableStateOf(true) }
    val coroutineScope = rememberCoroutineScope()
    
    val settings =
      rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
        UpdateManager.installLatestApk(context)
      }
    
    val installApkLauncher =
      rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
        if (result) {
          UpdateManager.installLatestApk(context)
        } else {
          if (!context.packageManager.canRequestPackageInstalls()) settings.launch(
            Intent(
              Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
              Uri.parse("package:${context.packageName}"),
            )
          )
          else UpdateManager.installLatestApk(context)
        }
      }
    
    // 根据下载情况显示对应消息
    val showToast = remember { mutableIntStateOf(0) }
    LaunchedEffect(key1 = showToast.intValue != 0) {
      when (showToast.intValue) {
        1 -> {
          MsgUtils.toast(context, "Already download,install now")
          showToast.intValue = 0
        }
        
        2 -> {
          MsgUtils.toast(context, "Download success,install now")
          showToast.intValue = 0
        }
        
        3 -> {
          MsgUtils.toast(context, "Download failed,check Internet")
          showToast.intValue = 0
        }
      }
    }
    
    Box(
      modifier = Modifier
        .fillMaxWidth()
        .padding(12.dp)
        .clip(RoundedCornerShape(8.dp))
        .background(primaryContainerLight)
    ) {
      Column(
        modifier = Modifier.padding(12.dp),
      ) {
        Row(
          verticalAlignment = Alignment.CenterVertically
        ) {
          Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier
            .clickable {
              if (enableButton) {
                enableButton = false
                coroutineScope.launch(Dispatchers.IO) {
                  runCatching {
                    if (FileUtils.fileExists(FileUtils.getExternalAppPath(context) + "/apks/latest.apk")) {
                      showToast.intValue = 1
                      installApkLauncher.launch(
                        Manifest.permission.REQUEST_INSTALL_PACKAGES
                      )
                      return@runCatching
                    }
                    
                    
                    checkForUpdate() ?: return@launch
                    val releaseDTO = getLatestRelease(true)
                    val downloadFileInfo = DownloadFileInfo(
                      downloadUrl = releaseDTO.assets[0].browserDownloadUrl,
                      saveFile = FileUtils.getFileExternal(
                        context, "apks", "latest.apk"
                      )
                    )
                    
                    FileApis
                      .downloadFile(
                        context = context,
                        downloadUrl = downloadFileInfo.downloadUrl,
                        saveFile = downloadFileInfo.saveFile,
                      ) { process, alreadyDownloadedSize ->
                        alreadyDownloadSize = alreadyDownloadedSize
                        currentProgress = process
                      }
                      .collect { infos ->
                        downloadFileStatus = infos
                        status = when (infos) {
                          is DownloadFileStatus.COMPLETED -> "Completed"
                          is DownloadFileStatus.INITIAL -> "UnStart"
                          is DownloadFileStatus.PROGRESS -> "Downloading"
                          is DownloadFileStatus.FAILED -> "Failed"
                        }
                        if (infos is DownloadFileStatus.COMPLETED) {
                          showToast.intValue = 2
                          installApkLauncher.launch(
                            Manifest.permission.REQUEST_INSTALL_PACKAGES
                          )
                        }
                      }
                  }.onFailure {
                    it.printStackTrace()
                    showToast.intValue = 3
                    return@launch
                  }
                  enableButton = true
                }
              }
            }
            .clip(
              RoundedCornerShape(8.dp)
            )
            .background(inversePrimaryLight)
            .padding(12.dp)
            .animateContentSize()) {
            Icon(
              imageVector = Icons.Default.Downloading,
              contentDescription = null,
              modifier = Modifier.padding(end = 12.dp),
            )
            Text(
              text = status,
              modifier = Modifier.align(Alignment.CenterVertically),
            )
          }
          
          AnimatedVisibility(visible = !enableButton) {
            Text(
              modifier = Modifier
                .animateContentSize()
                .padding(start = 12.dp),
              text = "${FileUtils.convertFileSize(alreadyDownloadSize)} / ${currentProgress}%"
            )
          }
        }
        Spacer(modifier = Modifier.padding(top = 8.dp))
        if (FileUtils.fileExists(FileUtils.getExternalAppPath(context) + "/apks/latest.apk")) {
          AlreadyDownloadComp(context)
        } else {
          LinearProgressIndicator(
            progress = { currentProgress / 100f },
            modifier = Modifier
              .padding(top = 12.dp)
              .fillMaxWidth(),
          )
        }
      }
      
    }
  }
  
  @Composable
  fun AlreadyDownloadComp(context: Context) {
    val showDialog = remember { mutableStateOf(false) }
    if (showDialog.value) {
      DialogUtils.DialogInfoComp(
        title = "Apk install request",
        content = "An apk was downloaded, do you want to remove it? \n" +
            "If you choose ok, you have to download a new apk and reboot app now!",
        onDismiss = {
          showDialog.value = false
        },
        onSuccess = {
          FileUtils.deleteFile(
            FileUtils.getExternalAppPath(context) + "/apks/latest.apk"
          )
          MsgUtils.toast(
            context, "Apk removed"
          )
          IntentUtils.restartApp(context)
        }
      )
    }
    
    ButtonWarning(
      title = "Apk already downloaded",
    ) {
      showDialog.value = true
    }
  }
  
  suspend fun getLatestRelease(allowBetas: Boolean): ReleaseDTO {
    val releases = withContext(Dispatchers.IO) {
      gitApis.getAllReleases("zhillerdev_compose_samples", "dev-template-basic")
    }
    // 过滤版本列表，根据是否允许测试版来决定过滤条件
    return releases.filter {
      // 记录日志以便调试
      Log.d("UpdateUtil:filter", it.name?.toString() ?: "null")
      if (!allowBetas && it.name != null) it.name.toVersion() is BuildVersion.Stable
      else true
      // 找到版本号最高的版本作为最新版本
    }.maxBy { it.name?.toVersion()?.toVersionNumber() ?: 0 }.also {
      // 记录日志以便调试
      Log.d("UpdateUtil:maxBy", it.name?.toVersion()?.toVersionString() ?: "null")
    }
  }
  
  /**
   * 检查是否有新版本可用
   *
   * 此函数负责检查应用的当前版本是否落后于GitHub上的最新发布版本如果允许测试版本（Betas），
   * 它们也会被考虑在内如果发现新版本，函数返回最新发布的版本信息；如果没有发现新版本或最新版本
   * 不高于当前版本，则返回null
   *
   * @param allowBetas 是否允许包括测试版本（Betas）在内的版本检查中，默认为true
   * @return 如果有新版本且版本高于当前版本，则返回最新版本的Release对象，否则返回null
   */
  suspend fun checkForUpdate(allowBetas: Boolean = true): ReleaseDTO? {
    // 将当前应用版本转换为Version对象以便比较
    val currentVersion = GlobalConfig.VERSION.toVersion()
    // 获取最新的发布版本，根据参数决定是否包括测试版本
    val latestRelease = getLatestRelease(allowBetas)
    // 将最新发布的版本名称转换为Version对象，以便与当前版本进行比较如果没有名称，则使用一个初始版本
    val latestVersion = latestRelease.name?.toVersion() ?: BuildVersion.Stable(0, 0, 0)
    // 比较最新版本和当前版本，如果最新版本较新，则返回最新发布的Release对象，否则返回null
    return if (latestVersion > currentVersion) {
      latestRelease
    } else {
      null
    }
  }

//  suspend fun downloadLatestApk(context: Context){
//
//  }
  
  /**
   * 在给定上下文中安装最新的APK文件
   * 此函数使用FileProvider获取最新APK的URI，并启动一个Intent来安装它
   * 如果在安装过程中发生任何错误，将捕获并打印异常
   *
   * @param context 上下文，用于访问应用程序特定资源和类，这里用于获取包名和启动安装Intent
   */
  fun installLatestApk(context: Context) = context.run {
    
    // 使用kotlin.runCatching来捕获并处理可能发生的异常
    kotlin.runCatching {
      // 获取最新APK文件的URI
      val contentUri = FileProvider.getUriForFile(
        this, "io.zhiller.simpletemplate.provider", getLatestApk()
      )
      // 创建用于安装APK的Intent
      val intent = Intent(Intent.ACTION_VIEW).apply {
        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        setDataAndType(contentUri, "application/vnd.android.package-archive")
      }
      // 启动安装Intent
      startActivity(intent)
    }.onFailure { throwable: Throwable ->
      // 打印异常堆栈跟踪，以便于调试和日志记录
      throwable.printStackTrace()
    }
  }
  
  /**
   * 获取最新的APK文件
   *
   * 该方法用于构建并返回一个代表最新APK文件的File对象这个方法特别有用
   * 在需要访问或操作应用程序的最新版本APK文件时，例如在升级或安装新版本的应用程序时
   *
   * @return File代表最新APK文件的File对象
   */
  private fun Context.getLatestApk() = File(getExternalFilesDir("apks"), "latest.apk")
}

